#include "StdAfx.h"
#include "SSLProtocolSocket.h"
#include "openssl\err.h"

CSSLProtocolSocket::CSSLProtocolSocket(void)
{
	m_sslInit = 0;
	m_ssl = NULL;
}

CSSLProtocolSocket::~CSSLProtocolSocket(void)
{
	Close();
}


//////////////////////////////////////////////////////////////////////////////////
//	初期化など
//////////////////////////////////////////////////////////////////////////////////
/*!
	閉じる
*/
void CSSLProtocolSocket::Close()
{
	//	親クラスを閉じる
	CProtocolSocket::Close();

	//	自分を閉じる
	if(m_sslInit)
	{
		SSL_shutdown(m_ssl);
		SSL_free(m_ssl);
	}
	m_sslInit = 0;

	//	このスレッドのエラー情報を破棄する
	ERR_remove_state(0);
}

//////////////////////////////////////////////////////////////////////////////////
//	通信処理
//////////////////////////////////////////////////////////////////////////////////
/*!
	SSL通信開始
*/
int CSSLProtocolSocket::SSLAccept(SSL_CTX *sslContext)
{
	//	再初期化？
	ASSERT(m_sslInit == 0);
	m_sslInit = 1;

	//	ソケットセットアップ
	m_bio = BIO_new_socket((int)m_socket,BIO_NOCLOSE);
	if(m_bio == NULL)
	{
		CloseError(CSPS_ERROR_CREATE_SSL_CONNECTION, "SSLAccept");
		return(-1);
	}

	//	SSL生成
	m_ssl=SSL_new(sslContext);
	if(m_ssl == NULL)
	{
		CloseError(CSPS_ERROR_CREATE_SSL_CONNECTION, "SSLAccept");
		return(-1);
	}

	//	BIO割り当て
	SSL_set_bio(m_ssl,m_bio,m_bio);

	//	ネゴシエーション
	while(1)
	{
		switch(SSL_get_error(m_ssl, SSL_accept(m_ssl)))
		{
		//	成功？
		case SSL_ERROR_NONE:
			return(0);

		//	リトライ
		case SSL_ERROR_WANT_READ:
		case SSL_ERROR_WANT_WRITE:
			if(WaitNextEvent(m_timeoutMS))	//	通信可能まで待つ
				return(-1);
			break;

		//	エラー
		case SSL_ERROR_ZERO_RETURN:
		default:
			CloseError(CSPS_ERROR_SSL_ACCEPT, "SSLAccept");
			return(-1);
		}
	}
}

//////////////////////////////////////////////////////////////////////////////////
//	通信処理
//////////////////////////////////////////////////////////////////////////////////
/*!
	送信を試す
*/
int CSSLProtocolSocket::BlockingSend(int timeOut)
{
	//	初期化チェック
	if(!m_sslInit)
	{
		CloseError(CBS_ERROR_CLOSE, "TrySend");
		return(-1);
	}

	//	データがあるだけループ
	while(m_sendBuf.GetInBuf() > 0)
	{
		//	送信用バッファ取得
		int		bufLen;
		char	*sendBuf = m_sendBuf.GetReadBuffer(&bufLen);

		//	送信を試みる
		int	ret = SSL_write(m_ssl, sendBuf, bufLen);
		switch(SSL_get_error(m_ssl, ret))
		{
		//	成功？
		case SSL_ERROR_NONE:
			m_sendBuf.SetReadedLen(ret);	//	送信したデータ量を設定
			return(m_sendBuf.GetInBuf());

		//	リトライ
		case SSL_ERROR_WANT_READ:
		case SSL_ERROR_WANT_WRITE:
			break;

		//	エラー
		case SSL_ERROR_ZERO_RETURN:
			CloseError(CBS_ERROR_CLOSE, "TrySend");
			return(-1);

		//	エラー
		default:
			CloseError(CBS_ERROR_OTHER, "TrySend");
			return(-1);
		}

		//	イベントを待つ
		if(WaitNextEvent(timeOut))
			return(-1);
	}

	return(0);
}


/*!
	受信を試す
*/
int CSSLProtocolSocket::BlockingRecv(int timeOut)
{
	//	初期化チェック
	if(!m_sslInit)
	{
		CloseError(CBS_ERROR_CLOSE, "TryRecv");
		return(-1);
	}

	//	空きはある？
	if(m_recvBuf.GetFreeBuf() == 0)
		return(m_recvBuf.GetInBuf());

	//	受信用バッファ取得
	int		bufLen;
	char	*recvBuf = m_recvBuf.GetWriteBuffer(&bufLen);

	//	受信成功までループ
	while(1)
	{
		//	送信してみる
		int ret = SSL_read(m_ssl, recvBuf, bufLen);
		switch(SSL_get_error(m_ssl, ret))
		{
		//	成功？
		case SSL_ERROR_NONE:
			m_recvBuf.SetWritedLen(ret);	//	受信量を設定
			return(m_recvBuf.GetInBuf());

		//	リトライ
		case SSL_ERROR_WANT_READ:
		case SSL_ERROR_WANT_WRITE:
			break;

		//	エラー
		case SSL_ERROR_ZERO_RETURN:
			CloseError(CBS_ERROR_CLOSE, "TryRecv");
			return(-1);

		//	エラー
		default:
			CloseError(CBS_ERROR_OTHER, "TryRecv");
			return(-1);
		}

		//	イベントを待つ
		if(WaitNextEvent(timeOut))
			return(-1);
	}
}


//////////////////////////////////////////////////////////////////////////////////
//	エラー
//////////////////////////////////////////////////////////////////////////////////
/*!
	エラーコードから文字列を取得
*/
CString CSSLProtocolSocket::GetErrorString(int errorCode)
{
	switch(errorCode)
	{
	case CSPS_ERROR_CREATE_SSL_CONNECTION:
		return(_T("SSL接続の初期化に失敗しました"));

	case CSPS_ERROR_SSL_ACCEPT:
		return(_T("SSL接続のネゴシエーションに失敗しました"));

	default:
		return(CProtocolSocket::GetErrorString(errorCode));
	}
}
