//
// This file is part of the imule Project.
//
// Copyright (c) 2004-2011 aMule Team ( admin@amule.org / http://www.amule.org )
// Copyright (c) 2004-2011 Marcelo Roberto 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
//

#ifndef __PROXY_H__
#define __PROXY_H__

#include <wx/wx.h>
#include "amuleIPV4Address.h"	// For amuleIPV4address
#include "StateMachine.h"	// For CStateMachine

/******************************************************************************/

/*
 * SOCKS4 protocol implementation according to:
 * - "SOCKS: A protocol for TCP proxy across firewalls":
 *   amule-root/docs/socks4.protocol
 */
const unsigned char SOCKS4_VERSION = 0x04;

const unsigned char SOCKS4_CMD_CONNECT	= 0x01;
const unsigned char SOCKS4_CMD_BIND	= 0x02;

const unsigned char SOCKS4_REPLY_CODE				= 0;
const unsigned char SOCKS4_REPLY_GRANTED			= 90;
const unsigned char SOCKS4_REPLY_FAILED				= 91;
const unsigned char SOCKS4_REPLY_FAILED_NO_IDENTD		= 92;
const unsigned char SOCKS4_REPLY_FAILED_DIFFERENT_USERIDS	= 93;

/*
 * SOCKS5 protocol implementation according to:
 * - RFC-1928: SOCKS Protocol Version 5
 * - RFC-1929: username/password Authentication for SOCKS V5
 *
 * Also, for the future :) :
 * - RFC-1961: GSS-API Authentication Method for SOCKS Version 5
 * - RFC-1508: Generic Security Service Application Program Interface
 * - RFC-1509: Genecic Security Service API: C-bindings
 *
 */

const unsigned char SOCKS5_VERSION = 0x05;

const unsigned char SOCKS5_AUTH_METHOD_NO_AUTH_REQUIRED		= 0x00;
const unsigned char SOCKS5_AUTH_METHOD_GSSAPI			= 0x01;
const unsigned char SOCKS5_AUTH_METHOD_USERNAME_PASSWORD	= 0x02;
const unsigned char SOCKS5_AUTH_METHOD_NO_ACCEPTABLE_METHODS	= 0xFF;

const unsigned char SOCKS5_AUTH_VERSION_USERNAME_PASSWORD	= 0x01;

const unsigned char SOCKS5_CMD_CONNECT		= 0x01;
const unsigned char SOCKS5_CMD_BIND		= 0x02;
const unsigned char SOCKS5_CMD_UDP_ASSOCIATE	= 0x03;

const unsigned char SOCKS5_RSV = 0x00;

const unsigned char SOCKS5_ATYP_IPV4_ADDRESS	= 0x01;
const unsigned char SOCKS5_ATYP_DOMAINNAME	= 0x03;
const unsigned char SOCKS5_ATYP_IPV6_ADDRESS	= 0x04;

const unsigned char SOCKS5_REPLY_SUCCEED		= 0x00;
const unsigned char SOCKS5_REPLY_GENERAL_SERVER_FAILURE	= 0x01;
const unsigned char SOCKS5_REPLY_CONNECTION_NOT_ALLOWED	= 0x02;
const unsigned char SOCKS5_REPLY_NETWORK_UNREACHABLE	= 0x03;
const unsigned char SOCKS5_REPLY_HOST_UNREACHABLE	= 0x04;
const unsigned char SOCKS5_REPLY_CONNECTION_REFUSED	= 0x05;
const unsigned char SOCKS5_REPLY_TTL_EXPIRED		= 0x06;
const unsigned char SOCKS5_REPLY_COMMAND_NOT_SUPPORTED	= 0x07;
const unsigned char SOCKS5_REPLY_ATYP_NOT_SUPPORTED	= 0x08;

//------------------------------------------------------------------------------
// CProxyType
//------------------------------------------------------------------------------

/*
 * These constants must match the integer values saved in the configuration file,
 * DO NOT CHANGE THIS ORDER!!!
 */
enum CProxyType {
        PROXY_NONE = -1,
        PROXY_SOCKS5,
        PROXY_SOCKS4,
        PROXY_HTTP,
        PROXY_SOCKS4a
};


//------------------------------------------------------------------------------
// CProxyData
//------------------------------------------------------------------------------
/**
 * The ProxyData class will hold information about the proxy server to be used.
 */
class CProxyData
{
public:
        /**
         * Default constructor.
         */
        CProxyData();
        /**
         * Constructor.
         *
         * @param proxyEnable	Whether proxy is enabled or not.
         * @param proxyType	The type of the proxy server.
         * @param proxyHostName	The proxy host name or IP address.
         * @param proxyPort	The proxy port number.
         * @param enablePassword Whether authentication should be performed.
         * @param userName	The user name to authenticate to the server.
         * @param password	The password to authenticate to the server.
         */
        CProxyData(
                bool		proxyEnable,
                CProxyType	proxyType,
                const wxString	&proxyHostName,
                unsigned short	proxyPort,
                bool		enablePassword,
                const wxString	&userName,
                const wxString	&password
        );
        /**
         * Clears the object contents.
         */
        void Clear();

public:
        //! Whether proxy is enabled or not.
        bool		m_proxyEnable;
        //! The type of the proxy server.
        CProxyType	m_proxyType;
        //! The proxy host name or IP address.
        wxString	m_proxyHostName;
        //! The proxy port number.
        unsigned short	m_proxyPort;
        //! Whether authentication should be performed.
        bool		m_enablePassword;
        //! The user name to authenticate to the server.
        wxString	m_userName;
        //! The password to authenticate to the server.
        wxString	m_password;
};

//------------------------------------------------------------------------------
// CProxyEventHandler
//------------------------------------------------------------------------------
/**
 * Event handler object used during proxy negotiation.
 */
class CProxyEventHandler : public wxEvtHandler
{
public:
        /**
         * Constructor.
         */
        CProxyEventHandler();

private:
        /**
         * Event handler function.
         */
        void ProxySocketHandler(wxSocketEvent &event);
        DECLARE_EVENT_TABLE()
};

//------------------------------------------------------------------------------
// CProxyStateMachine
//------------------------------------------------------------------------------
/* This size is just to be a little bit greater than the UDP buffer used in aMule.
 * Proxy protocol needs much less than this. 1024 would be ok. Other options are
 * - Default ethernet MTU - Eth-II - IP - UDP: 1,514 - 14 - 20 - 8 = 1472 bytes;
 * - Default token ring MTU 4,202 - overheads = ??.
 * It would be really more efficient if the final object was less than
 * a page (4096 bytes) in size.
 */
//const unsigned int PROXY_BUFFER_SIZE = 1024;
const unsigned int PROXY_BUFFER_SIZE = 5*1024;

enum CProxyCommand {
        PROXY_CMD_CONNECT,
        PROXY_CMD_BIND,
        PROXY_CMD_UDP_ASSOCIATE
};

enum CProxyState {
        PROXY_STATE_START = 0,
        PROXY_STATE_END = 1
};

/**
 * The ProxyStateMachine class is the ancestor of all proxy classes.
 *
 * CProxyStateMachine will do all the common work that a proxy class must do
 * and provide the necessary variables.
 */
class CProxyStateMachine : public CStateMachine
{
public:
        /**
         * Constructor.
         *
         * @param name		The name of the state machine. For debug messages only.
         * @param max_states	The maximum number of states that this machine will have.
         * @param proxyData	The necessary proxy information.
         * @param cmd		The type of proxy command to run.
         */
        CProxyStateMachine(
                wxString name,
                const unsigned int max_states,
                const CProxyData &proxyData,
                CProxyCommand cmd);
        /**
         * Destructor.
         */
        virtual ~CProxyStateMachine();
        /**
         * Adds a small string to the state machine name, containing the proxy command.
         *
         * @param s	The original state machine name.
         * @param cmd	The proxy command.
         */
        static wxString	&NewName(wxString &s, CProxyCommand cmd);

        /* Interface */
        bool		Start(const wxIPaddress &peerAddress, wxSocketClient *proxyClientSocket);
        t_sm_state	HandleEvent(t_sm_event event);
        void		AddDummyEvent();
        void		ReactivateSocket();
        char 		*GetBuffer()				{ return m_buffer; }
        wxIPaddress	&GetProxyBoundAddress(void) const	{ return *m_proxyBoundAddress; }
        unsigned char	GetLastReply(void) const		{ return m_lastReply; }
        bool		IsEndState() const			{ return GetState() == PROXY_STATE_END; }

protected:
        wxSocketBase		&ProxyWrite(wxSocketBase &socket, const void *buffer, wxUint32 nbytes);
        wxSocketBase		&ProxyRead(wxSocketBase &socket, void *buffer);
        bool		CanReceive() const;
        bool		CanSend() const;
        //
        // Initialized at constructor
        //
        const CProxyData	&m_proxyData;
        CProxyCommand		m_proxyCommand;
        //
        // Member variables
        //
        char			m_buffer[PROXY_BUFFER_SIZE];
        bool			m_isLost;
        bool			m_isConnected;
        bool			m_canReceive;
        bool			m_canSend;
        bool			m_ok;
        unsigned int		m_lastRead;
        unsigned int		m_lastWritten;
        wxSocketError		m_lastError;
        //
        // Will be initialized at Start()
        //
        wxIPaddress		*m_peerAddress;
        wxSocketClient		*m_proxyClientSocket;
        wxIPaddress		*m_proxyBoundAddress;
        amuleIPV4Address	m_proxyBoundAddressIPV4;
        //wxIPV6address		m_proxyBoundAddressIPV6;
        //
        // Temporary variables
        //
        unsigned char		m_lastReply;
        unsigned int		m_packetLenght;
};

//------------------------------------------------------------------------------
// CSocks5StateMachine
//------------------------------------------------------------------------------
class CSocks5StateMachine;
typedef void (CSocks5StateMachine::*Socks5StateProcessor)(bool entry);
class CSocks5StateMachine : public CProxyStateMachine
{
private:
        static const unsigned int SOCKS5_MAX_STATES = 14;

        enum Socks5State {
                SOCKS5_STATE_START = PROXY_STATE_START,
                SOCKS5_STATE_END = PROXY_STATE_END,
                SOCKS5_STATE_SEND_QUERY_AUTHENTICATION_METHOD,
                SOCKS5_STATE_RECEIVE_AUTHENTICATION_METHOD,
                SOCKS5_STATE_PROCESS_AUTHENTICATION_METHOD,
                SOCKS5_STATE_SEND_AUTHENTICATION_GSSAPI,
                SOCKS5_STATE_RECEIVE_AUTHENTICATION_GSSAPI,
                SOCKS5_STATE_PROCESS_AUTHENTICATION_GSSAPI,
                SOCKS5_STATE_SEND_AUTHENTICATION_USERNAME_PASSWORD,
                SOCKS5_STATE_RECEIVE_AUTHENTICATION_USERNAME_PASSWORD,
                SOCKS5_STATE_PROCESS_AUTHENTICATION_USERNAME_PASSWORD,
                SOCKS5_STATE_SEND_COMMAND_REQUEST,
                SOCKS5_STATE_RECEIVE_COMMAND_REPLY,
                SOCKS5_STATE_PROCESS_COMMAND_REPLY
        };

public:
        /* Constructor */
        CSocks5StateMachine(
                const CProxyData &proxyData,
                CProxyCommand proxyCommand);
        void process_state(t_sm_state state, bool entry);
        t_sm_state next_state(t_sm_event event);

private:
        /* State Processors */
        void process_start(bool entry);
        void process_send_query_authentication_method(bool entry);
        void process_receive_authentication_method(bool entry);
        void process_process_authentication_method(bool entry);
        void process_send_authentication_gssapi(bool entry);
        void process_receive_authentication_gssapi(bool entry);
        void process_process_authentication_gssapi(bool entry);
        void process_send_authentication_username_password(bool entry);
        void process_receive_authentication_username_password(bool entry);
        void process_process_authentication_username_password(bool entry);
        void process_send_command_request(bool entry);
        void process_receive_command_reply(bool entry);
        void process_process_command_reply(bool entry);
        void process_end(bool entry);
        /* Private Vars */
        Socks5StateProcessor m_process_state[SOCKS5_MAX_STATES];
        wxString m_state_name[SOCKS5_MAX_STATES];
};

//------------------------------------------------------------------------------
// CSocks4StateMachine
//------------------------------------------------------------------------------
class CSocks4StateMachine;
typedef void (CSocks4StateMachine::*Socks4StateProcessor)(bool entry);
class CSocks4StateMachine : public CProxyStateMachine
{
private:
        static const unsigned int SOCKS4_MAX_STATES = 5;

        enum Socks4State {
                SOCKS4_STATE_START = PROXY_STATE_START,
                SOCKS4_STATE_END = PROXY_STATE_END,
                SOCKS4_STATE_SEND_COMMAND_REQUEST,
                SOCKS4_STATE_RECEIVE_COMMAND_REPLY,
                SOCKS4_STATE_PROCESS_COMMAND_REPLY
        };

public:
        /* Constructor */
        CSocks4StateMachine(
                const CProxyData &proxyData,
                CProxyCommand proxyCommand);
        void process_state(t_sm_state state, bool entry);
        t_sm_state next_state(t_sm_event event);

private:
        /* State Processors */
        void process_start(bool entry);
        void process_send_command_request(bool entry);
        void process_receive_command_reply(bool entry);
        void process_process_command_reply(bool entry);
        void process_end(bool entry);
        /* Private Vars */
        Socks4StateProcessor m_process_state[SOCKS4_MAX_STATES];
        wxString m_state_name[SOCKS4_MAX_STATES];
};

//------------------------------------------------------------------------------
// CHttpStateMachine
//------------------------------------------------------------------------------
class CHttpStateMachine;
typedef void (CHttpStateMachine::*HttpStateProcessor)(bool entry);
class CHttpStateMachine : public CProxyStateMachine
{
private:
        static const unsigned int HTTP_MAX_STATES = 5;

        enum HttpState {
                HTTP_STATE_START = PROXY_STATE_START,
                HTTP_STATE_END = PROXY_STATE_END,
                HTTP_STATE_SEND_COMMAND_REQUEST,
                HTTP_STATE_RECEIVE_COMMAND_REPLY,
                HTTP_STATE_PROCESS_COMMAND_REPLY
        };

public:
        /* Constructor */
        CHttpStateMachine(
                const CProxyData &proxyData,
                CProxyCommand proxyCommand);
        void process_state(t_sm_state state, bool entry);
        t_sm_state next_state(t_sm_event event);

private:
        /* State Processors */
        void process_start(bool entry);
        void process_send_command_request(bool entry);
        void process_receive_command_reply(bool entry);
        void process_process_command_reply(bool entry);
        void process_end(bool entry);
        /* Private Vars */
        HttpStateProcessor m_process_state[HTTP_MAX_STATES];
        wxString m_state_name[HTTP_MAX_STATES];
};

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

class CDatagramSocketProxy;

class CProxySocket : public wxSocketClient
{
        friend class CProxyEventHandler;
public:
        /* Constructor */
        CProxySocket(
                wxSocketFlags flags = wxSOCKET_NONE,
                const CProxyData *proxyData = NULL,
                CProxyCommand proxyCommand = PROXY_CMD_CONNECT,
                CDatagramSocketProxy *udpSocket = NULL);

        /* Destructor */
        ~CProxySocket();

        /* I know, this is not very good, because SetEventHandler is not
         * virtual in wxSocketBase, but I need to GetEventHandler in Proxy.cpp,
         * so...
         */
        void SetEventHandler(wxEvtHandler &handler, int id = wxID_ANY) {
                m_socketEventHandler = &handler;
                m_socketEventHandlerId = id;
                wxSocketClient::SetEventHandler(handler, id);
        }
        wxEvtHandler *GetEventHandler(void)	const { return m_socketEventHandler; }
        int GetEventHandlerId(void)		const { return m_socketEventHandlerId; }
        void SaveEventHandler(void) {
                m_savedSocketEventHandler = m_socketEventHandler;
                m_savedSocketEventHandlerId = m_socketEventHandlerId;
        }
        void RestoreEventHandler(void) {
                m_socketEventHandler = m_savedSocketEventHandler;
                m_socketEventHandlerId = m_savedSocketEventHandlerId;
                SetEventHandler(*m_socketEventHandler, m_socketEventHandlerId);
        }

        /* Interface */
        void		SetProxyData(const CProxyData *proxyData);
        bool		GetUseProxy() const	{ return m_useProxy; }
        char 		*GetBuffer()		{ return m_proxyStateMachine->GetBuffer(); }
        wxIPaddress	&GetProxyBoundAddress(void) const
        { return m_proxyStateMachine->GetProxyBoundAddress(); }
        bool Start(const wxIPaddress &peerAddress);
        bool ProxyIsCapableOf(CProxyCommand proxyCommand) const;
        bool ProxyNegotiationIsOver() const	{ return m_proxyStateMachine->IsEndState(); }
        CDatagramSocketProxy *GetUDPSocket() const { return m_udpSocket; }

private:
        bool			m_useProxy;
        CProxyData		m_proxyData;
        amuleIPV4Address	m_proxyAddress;
        CProxyStateMachine	*m_proxyStateMachine;
        CDatagramSocketProxy	*m_udpSocket;
        wxEvtHandler		*m_socketEventHandler;
        int			m_socketEventHandlerId;
        wxEvtHandler		*m_savedSocketEventHandler;
        int			m_savedSocketEventHandlerId;
};

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

class CSocketClientProxy : public CProxySocket
{
public:
        /* Constructor */
        CSocketClientProxy(
                wxSocketFlags flags = wxSOCKET_NONE,
                const CProxyData *proxyData = NULL);

        /* Interface */
        bool Connect(wxIPaddress &address, bool wait);
        CSocketClientProxy& Read(void *buffer, wxUint32 nbytes);
        CSocketClientProxy& Write(const void *buffer, wxUint32 nbytes);

private:
        wxMutex			m_socketLocker;
};

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

class CSocketServerProxy : public wxSocketServer
{
public:
        /* Constructor */
        CSocketServerProxy(
                wxIPaddress &address,
                wxSocketFlags flags = wxSOCKET_NONE,
                const CProxyData *proxyData = NULL);

        /* Interface */
        CSocketServerProxy& Read(void *buffer, wxUint32 nbytes);
        CSocketServerProxy& Write(const void *buffer, wxUint32 nbytes);

private:
        wxMutex			m_socketLocker;
};

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

enum UDPOperation {
        UDP_OPERATION_NONE,
        UDP_OPERATION_RECV_FROM,
        UDP_OPERATION_SEND_TO
};

const unsigned int PROXY_UDP_OVERHEAD_IPV4 		= 10;
const unsigned int PROXY_UDP_OVERHEAD_DOMAIN_NAME	= 262;
const unsigned int PROXY_UDP_OVERHEAD_IPV6		= 20;
const unsigned int PROXY_UDP_MAXIMUM_OVERHEAD		= PROXY_UDP_OVERHEAD_DOMAIN_NAME;

class CDatagramSocketProxy : public wxDatagramSocket
{
public:
        /* Constructor */
        CDatagramSocketProxy(
                wxIPaddress &address,
                wxSocketFlags flags = wxSOCKET_NONE,
                const CProxyData *proxyData = NULL);

        /* Destructor */
        ~CDatagramSocketProxy();

        /* Interface */
        void SetUDPSocketOk() { m_udpSocketOk = true; }

        /* wxDatagramSocket Interface */
        virtual wxDatagramSocket& RecvFrom(
                wxSockAddress& addr, void* buf, wxUint32 nBytes );
        virtual wxDatagramSocket& SendTo(
                wxIPaddress& addr, const void* buf, wxUint32 nBytes );
        virtual wxUint32 LastCount(void) const;

private:
        bool			m_udpSocketOk;
        CProxySocket		m_proxyTCPSocket;
        enum UDPOperation	m_lastUDPOperation;
        unsigned int		m_lastUDPOverhead;
        wxMutex			m_socketLocker;
};

/******************************************************************************/

#endif /* __PROXY_H__ */
// File_checked_for_headers
