/*
 * pqisslproxy.cc
 *
 * 3P/PQI network interface for RetroShare.
 *
 * Copyright 2004-2013 by Robert Fernie.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License Version 2.1 as published by the Free Software Foundation.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA.
 *
 * Please report all bugs and problems to "retroshare@lunamutt.com".
 *
 */



#include "pqi/pqisslproxy.h"
#include "pqi/pqinetwork.h"

#include <errno.h>
#include <openssl/err.h>

#include <sstream>

#include "util/rsdebug.h"
#include "util/rsnet.h"

#include "pqi/p3linkmgr.h"

static struct RsLog::logInfo pqisslproxyzoneInfo = {RsLog::Default, "pqisslproxy"};
#define pqisslproxyzone &pqisslproxyzoneInfo

// #define PROXY_DEBUG	1

#define PROXY_STATE_FAILED			0
#define PROXY_STATE_INIT			1
#define PROXY_STATE_WAITING_METHOD_RESPONSE	2
#define PROXY_STATE_WAITING_SOCKS_RESPONSE	3
#define PROXY_STATE_CONNECTION_COMPLETE		4

pqisslproxy::pqisslproxy(pqissllistener *l, PQInterface *parent, p3LinkMgr *lm)
        :pqissl(l, parent, lm)
{
	sockaddr_storage_clear(remote_addr);
	return;
}


pqisslproxy::~pqisslproxy()
{ 
        rslog(RSL_ALERT, pqisslproxyzone,  
            "pqisslproxy::~pqisslproxy -> destroying pqisslproxy");

	stoplistening();
	reset();

	return;
}

int 	pqisslproxy::Initiate_Connection()
{

#ifdef PROXY_DEBUG
	std::cerr << "pqisslproxy::Initiate_Connection()";
	std::cerr << std::endl;
#endif

  	rslog(RSL_DEBUG_BASIC, pqisslproxyzone, 
		 "pqisslproxy::Initiate_Connection() Connection to Proxy");
	/* init proxy state */
	mProxyState = PROXY_STATE_INIT;

	/* call standard Init_Conn() */
	return pqissl::Initiate_Connection();
}


/********* VERY DIFFERENT **********/
int 	pqisslproxy::Basic_Connection_Complete()
{
	rslog(RSL_DEBUG_BASIC, pqisslproxyzone, 
	  "pqisslproxy::Basic_Connection_Complete()...");

#ifdef PROXY_DEBUG
	std::cerr << "pqisslproxy::Basic_Connection_Complete() STATE: " << mProxyState;
	std::cerr << std::endl;
#endif

	if (CheckConnectionTimeout())
	{
		// calls reset.
		return -1;
	}

	int ret = 0;
	switch(mProxyState)
	{
		case PROXY_STATE_INIT:
			ret = Proxy_Send_Method(); // checks basic conn, sends Method when able.
			break;

		case PROXY_STATE_WAITING_METHOD_RESPONSE:
			ret = Proxy_Send_Address(); // waits for Method Response, send Address when able.
			break;

		case PROXY_STATE_WAITING_SOCKS_RESPONSE: 
			ret = Proxy_Connection_Complete(); // wait for ACK.
			break;

		case PROXY_STATE_CONNECTION_COMPLETE:

#ifdef PROXY_DEBUG
			std::cerr << "pqisslproxy::Basic_Connection_Complete() COMPLETED";
			std::cerr << std::endl;
#endif

			return 1;

		case PROXY_STATE_FAILED:

#ifdef PROXY_DEBUG
			std::cerr << "pqisslproxy::Basic_Connection_Complete() FAILED";
			std::cerr << std::endl;
#endif

			reset_locked();
			return -1;
	}

	if (ret < 0)
	{

#ifdef PROXY_DEBUG
		std::cerr << "pqisslproxy::Basic_Connection_Complete() FAILED(2)";
		std::cerr << std::endl;
#endif
		reset_locked();
		return -1; // FAILURE.
	}


#ifdef PROXY_DEBUG
	std::cerr << "pqisslproxy::Basic_Connection_Complete() IN PROGRESS";
	std::cerr << std::endl;
#endif

	// In Progress.
	return 0;
}


int 	pqisslproxy::Proxy_Send_Method()
{

#ifdef PROXY_DEBUG
	std::cerr << "pqisslproxy::Proxy_Send_Method() Checking pqissl::Basic_Connection_Complete()";
	std::cerr << std::endl;
#endif

	int ret = pqissl::Basic_Connection_Complete();
	if (ret != 1)
	{
		return ret; // basic connection not complete.
	}

#ifdef PROXY_DEBUG
	std::cerr << "pqisslproxy::Proxy_Send_Method() Basic complete, sending Method";
	std::cerr << std::endl;
#endif

	/* send hello to proxy server */
	char method_hello_data[3] = { 0x05, 0x01, 0x00 };  // [ Ver | nMethods (1) | No Auth Method ]

	int sent = send(sockfd, method_hello_data, 3, 0);
	if (sent != 3)
	{

#ifdef PROXY_DEBUG
		std::cerr << "pqisslproxy::Proxy_Send_Method() Send Failure";
		std::cerr << std::endl;
#endif
		return -1;
	}

#ifdef PROXY_DEBUG
	std::cerr << "pqisslproxy::Proxy_Send_Method() Send Method Okay";
	std::cerr << std::endl;
#endif

	mProxyState = PROXY_STATE_WAITING_METHOD_RESPONSE;

	return 1;
}

int 	pqisslproxy::Proxy_Method_Response()
{

#ifdef PROXY_DEBUG
	std::cerr << "pqisslproxy::Proxy_Method_Response()";
	std::cerr << std::endl;
#endif

	/* get response from proxy server */

	char method_response[2];

    /*
      first it was:

          int recvd = recv(sockfd, method_response, 2, MSG_WAITALL);

      this does not work on windows, because the socket is in nonblocking mode
      the winsock reference says about the recv function and MSG_WAITALL:

      "Note that if the underlying transport does not support MSG_WAITALL,
       or if the socket is in a non-blocking mode, then this call will fail with WSAEOPNOTSUPP."

       now it is a two step process:

           int recvd = recv(sockfd, method_response, 2, MSG_PEEK); // test how many bytes are in the input queue
           if (enaugh bytes available){
               recvd = recv(sockfd, method_response, 2, 0);
           }

       this does not work on windows:
           if ((recvd == -1) && (errno == EAGAIN)) return TRY_AGAIN_LATER;

       instead have to do:
           if ((recvd == -1) && (WSAGetLastError() == WSAEWOULDBLOCK)) return TRY_AGAIN_LATER;
     */

    // test how many bytes can be read from the queue
    int recvd = recv(sockfd, method_response, 2, MSG_PEEK);
	if (recvd != 2)
	{
#ifdef WINDOWS_SYS
        if ((recvd == -1) && (WSAGetLastError() == WSAEWOULDBLOCK))
        {
#ifdef PROXY_DEBUG
            std::cerr << "pqisslproxy::Proxy_Method_Response() waiting for more data (windows)";
            std::cerr << std::endl;
#endif
            return 0;
        }
#endif
		if ((recvd == -1) && (errno == EAGAIN))
		{

#ifdef PROXY_DEBUG
			std::cerr << "pqisslproxy::Proxy_Method_Response() EAGAIN";
			std::cerr << std::endl;
#endif

			return 0;
        }
        else if (recvd == -1)
        {
#ifdef PROXY_DEBUG
            std::cerr << "pqisslproxy::Proxy_Method_Response() recv error peek";
            std::cerr << std::endl;
#endif
            return -1;
        }
        else
        {
#ifdef PROXY_DEBUG
            std::cerr << "pqisslproxy::Proxy_Method_Response() waiting for more data";
            std::cerr << std::endl;
#endif
            return 0;
        }
	}

    // read the bytes
    recvd = recv(sockfd, method_response, 2, 0);
    if (recvd != 2)
    {
#ifdef PROXY_DEBUG
        std::cerr << "pqisslproxy::Proxy_Method_Response() recv error";
        std::cerr << std::endl;
#endif
        return -1;
    }

	// does it make sense?
	if (method_response[0] != 0x05)
	{

		// Error.
#ifdef PROXY_DEBUG
		std::cerr << "pqisslproxy::Proxy_Method_Response() Error response[0] != 0x05. Is: ";
		std::cerr << (uint32_t) method_response[0];
		std::cerr << std::endl;
#endif
		return -1;
	}

	if (method_response[1] != 0x00)
	{

#ifdef PROXY_DEBUG
		std::cerr << "pqisslproxy::Proxy_Method_Response() Error response[0] != 0x00. Is: ";
		std::cerr << (uint32_t) method_response[1];
		std::cerr << std::endl;
#endif
		// Error.
		return -1;
	}


#ifdef PROXY_DEBUG
	std::cerr << "pqisslproxy::Proxy_Method_Response() Response Okay";
	std::cerr << std::endl;
#endif

	return 1;
}

#define MAX_SOCKS_REQUEST_LEN  262  // 4 + 1 + 255 + 2.

int 	pqisslproxy::Proxy_Send_Address()
{

#ifdef PROXY_DEBUG
	std::cerr << "pqisslproxy::Proxy_Send_Address() Checking Method Response";
	std::cerr << std::endl;
#endif

	// Check Method Response.
	int ret = Proxy_Method_Response();
	if (ret != 1)
	{
		return ret; // Method Response not complete.
	}

	char socks_request[MAX_SOCKS_REQUEST_LEN] =
		{ 0x05, // SOCKS VERSION.
		0x01, // CONNECT (Tor doesn't support BIND or UDP).
		0x00, // RESERVED.
		0x03, // ADDRESS TYPE (Domain Name)
		0x00, // Length of Domain name... the rest is variable so can't hard code it!
		};

	/* get the length of the domain name, pack so we can't overflow uint8_t */
	uint8_t len = mDomainAddress.length();
	socks_request[4] = len;
	for(int i = 0; i < len; i++)
	{
		socks_request[5 + i] = mDomainAddress[i];
	}

	/* now add the port, being careful with packing */
	uint16_t net_port = htons(mRemotePort);
	socks_request[5 + len] = ((uint8_t *) &net_port)[0];
	socks_request[5 + len + 1] = ((uint8_t *) &net_port)[1];

	int pkt_len = 5 + len + 2;

#ifdef PROXY_DEBUG
	std::cerr << "pqisslproxy::Proxy_Send_Address() Sending String: ";
	for(int i = 0; i < pkt_len; i++)
		std::cerr << (uint32_t) socks_request[i];
	std::cerr << std::endl;
#endif
	int sent = send(sockfd, socks_request, pkt_len, 0);
	if (sent != pkt_len)
	{

#ifdef PROXY_DEBUG
		std::cerr << "pqisslproxy::Proxy_Send_Address() Send Error";
		std::cerr << std::endl;
#endif

		return -1;
	}


#ifdef PROXY_DEBUG
	std::cerr << "pqisslproxy::Proxy_Send_Address() Sent Okay";
	std::cerr << std::endl;
#endif

	mProxyState = PROXY_STATE_WAITING_SOCKS_RESPONSE;
	return 1;
}

int 	pqisslproxy::Proxy_Connection_Complete()
{
	/* get response from proxy server */
	/* response is similar format to request - with variable length data */

	char socks_response[MAX_SOCKS_REQUEST_LEN];

    // test how many bytes can be read
    int recvd = recv(sockfd, socks_response, 5, MSG_PEEK);
	if (recvd != 5)
	{
#ifdef WINDOWS_SYS
        if ((recvd == -1) && (WSAGetLastError() == WSAEWOULDBLOCK))
        {
#ifdef PROXY_DEBUG
            std::cerr << "pqisslproxy::Proxy_Connection_Complete() waiting for more data (windows)";
            std::cerr << std::endl;
#endif
            return 0;
        }
#endif
		if ((recvd == -1) && (errno == EAGAIN))
		{
#ifdef PROXY_DEBUG
			std::cerr << "pqisslproxy::Proxy_Connection_Complete() EAGAIN";
			std::cerr << std::endl;
#endif
			return 0;
		}
        else if (recvd == -1)
        {
#ifdef PROXY_DEBUG
            std::cerr << "pqisslproxy::Proxy_Connection_Complete() recv error peek";
            std::cerr << std::endl;
#endif
            return -1;
        }
        else
        {
#ifdef PROXY_DEBUG
            std::cerr << "pqisslproxy::Proxy_Connection_Complete() waiting for more data";
            std::cerr << std::endl;
#endif
            return 0;
        }
	}

    // read the bytes
    recvd = recv(sockfd, socks_response, 5, 0);
    if (recvd != 5)
    {
#ifdef PROXY_DEBUG
        std::cerr << "pqisslproxy::Proxy_Connection_Complete() recv error";
        std::cerr << std::endl;
#endif
        return -1;
    }
			
	// error checking.
	if (socks_response[0] != 0x05)
	{

#ifdef PROXY_DEBUG
		std::cerr << "pqisslproxy::Proxy_Connection_Complete() ERROR socks_response[0] != 0x05. is: ";
		std::cerr << (uint32_t) socks_response[0];
		std::cerr << std::endl;
#endif

		// error.
		return -1;
	}

	if (socks_response[1] != 0x00)
	{

#ifdef PROXY_DEBUG
		std::cerr << "pqisslproxy::Proxy_Connection_Complete() ERROR socks_response[1] != 0x00. is: ";
		std::cerr << (uint32_t) socks_response[1];
		std::cerr << std::endl;
#endif

		// connection failed.
		return -1;
	}

	int address_bytes = 0;
	switch(socks_response[3]) // Address Type.
	{
		case 0x01:
			// IPv4 4 address bytes.
			address_bytes = 4;
#ifdef PROXY_DEBUG
			std::cerr << "pqisslproxy::Proxy_Connection_Complete() IPv4 Address Type";
			std::cerr << std::endl;
#endif
			break;
		case 0x04:
			// IPv6 16 address bytes.
			address_bytes = 16;
#ifdef PROXY_DEBUG
			std::cerr << "pqisslproxy::Proxy_Connection_Complete() IPv6 Address Type";
			std::cerr << std::endl;
#endif
			break;
		case 0x03:
			// Variable address bytes - specified in next byte.
			address_bytes = 1 + socks_response[4];
#ifdef PROXY_DEBUG
			std::cerr << "pqisslproxy::Proxy_Connection_Complete() Domain Address Type. len: " << address_bytes;
			std::cerr << std::endl;
#endif
			break;
		default:
			// unknown error.
#ifdef PROXY_DEBUG
			std::cerr << "pqisslproxy::Proxy_Connection_Complete() ERROR Unknown Address Type";
			std::cerr << std::endl;
#endif
			return -1;
			break;
	}


    // test how many bytes can be read
    recvd = recv(sockfd, &(socks_response[5]), address_bytes + 1, MSG_PEEK); // address_bytes - 1 + 2...
	if (recvd != address_bytes + 1)
	{
#ifdef WINDOWS_SYS
        if((recvd == -1) && (WSAGetLastError() == WSAEWOULDBLOCK))
        {
#ifdef PROXY_DEBUG
            std::cerr << "pqisslproxy::Proxy_Connection_Complete() waiting for more data(2) (windows)";
            std::cerr << std::endl;
#endif
            return 0;
        }
#endif
		if ((recvd == -1) && (errno == EAGAIN))
		{
#ifdef PROXY_DEBUG
			std::cerr << "pqisslproxy::Proxy_Connection_Complete() ERROR EAGAIN at end.";
			std::cerr << std::endl;
#endif
			// Waiting - shouldn't happen.
			return 0;
		}
        else if (recvd == -1)
        {
#ifdef PROXY_DEBUG
            std::cerr << "pqisslproxy::Proxy_Connection_Complete() ERROR recving(2)";
            std::cerr << std::endl;
#endif
            return -1;
        }
        else
        {
#ifdef PROXY_DEBUG
            std::cerr << "pqisslproxy::Proxy_Connection_Complete() waiting for more data(2)";
            std::cerr << std::endl;
#endif
            return 0;
        }
	}

    // read the bytes
    recvd = recv(sockfd, &(socks_response[5]), address_bytes + 1, 0); // address_bytes - 1 + 2...
    if (recvd != address_bytes + 1)
    {
#ifdef PROXY_DEBUG
        std::cerr << "pqisslproxy::Proxy_Connection_Complete() recv error (2)";
        std::cerr << std::endl;
#endif
        return -1;
    }

#ifdef PROXY_DEBUG
	std::cerr << "pqisslproxy::Proxy_Connection_Complete() Received String: ";
	for(int i = 0; i < 4 + address_bytes + 2; i++)
		std::cerr << (uint32_t) socks_response[i];
	std::cerr << std::endl;
#endif

	// should print address.
	// if we get here - connection is good!.
	mProxyState = PROXY_STATE_CONNECTION_COMPLETE;
	return 1;

}

bool pqisslproxy::connect_parameter(uint32_t type, const std::string &value)
{
	{
		RsStackMutex stack(mSslMtx); /**** LOCKED MUTEX ****/
	
	        if (type == NET_PARAM_CONNECT_DOMAIN_ADDRESS)
	        {
	                std::string out;
	                rs_sprintf(out, "pqisslproxy::connect_parameter() Peer: %s DOMAIN_ADDRESS: %s", PeerId().toStdString().c_str(), value.c_str());
	                rslog(RSL_WARNING, pqisslproxyzone, out);
	
	                mDomainAddress = value;
#ifdef PROXY_DEBUG
	                std::cerr << out << std::endl;
#endif
	                return true;
	        }
	}

        return pqissl::connect_parameter(type, value);
}

bool pqisslproxy::connect_parameter(uint32_t type, uint32_t value)
{
	{
		RsStackMutex stack(mSslMtx); /**** LOCKED MUTEX ****/

	        if (type == NET_PARAM_CONNECT_REMOTE_PORT)
	        {
	                std::string out;
	                rs_sprintf(out, "pqisslproxy::connect_parameter() Peer: %s REMOTE_PORT: %lu", PeerId().toStdString().c_str(), value);
	                rslog(RSL_WARNING, pqisslproxyzone, out);
	
	        	mRemotePort = value;
#ifdef PROXY_DEBUG
	                std::cerr << out << std::endl;
#endif
	                return true;
	        }
	}
        return pqissl::connect_parameter(type, value);
}




