#include "StdAfx.h"
#include "SSLProtocolSocket.h"
#include "SSLOverlappedSocketBIO.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;

	//	OverlappedSocket対応BIOを生成
	m_bio = BIO_new_ovrs(&m_socket);
	if(m_bio == NULL)
	{
		CloseError(CSPS_ERROR_CREATE_SSL_CONNECTION, _T("SSLAccept"));
		return(-1);
	}

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

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

	//	ネゴシエーション
	int	sslErr = SSL_get_error(m_ssl, SSL_accept(m_ssl));
	if(sslErr == SSL_ERROR_ZERO_RETURN)
	{
		//	SSLプロトコル上で切断を検知
		CloseError(CBS_ERROR_CLOSE, "SSLAccept");
		return(-1);
	}
	else if(sslErr == SSL_ERROR_SSL)
	{
		//	SSLプロトコルエラー
		CloseError(CSPS_ERROR_SSL_ACCEPT, "SSLAccept");
		return(-1);
	}
	else if(sslErr != SSL_ERROR_NONE)
	{
		switch(m_socket.GetLastError())
		{
		case COverlappedSocket::OVRS_ERROR_TIMEOUT:
			CloseError(CBS_ERROR_TIMEOUT, _T("SSLAccept"));
			break;

		case COverlappedSocket::OVRS_ERROR_CLOSE:
			CloseError(CBS_ERROR_CLOSE, _T("SSLAccept"));
			break;

		case COverlappedSocket::OVRS_ERROR_BREAK:
			CloseError(CBS_ERROR_BREAK, _T("SSLAccept"));
			break;

		case COverlappedSocket::OVRS_ERROR_OTHER:
			CloseError(CBS_ERROR_OTHER, _T("SSLAccept"));
			break;

		case COverlappedSocket::OVRS_ERROR_NO_ERROR:
		default:
			CloseError(CSPS_ERROR_SSL_OTHER, _T("SSLAccept"));
			break;
		}
		return(-1);
	}
	return(0);
}

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

	//	データがある?
	if(m_sendBuf.GetInBuf() == 0)
		return(0);

	//	タイムアウト指定
	m_socket.SetTimeOut(timeOut);

	//	送信用バッファ取得
	int		bufLen;
	char	*sendBuf = m_sendBuf.GetReadBuffer(&bufLen);

	//	送信
	int sended = SSL_write(m_ssl, sendBuf, bufLen);

	//	エラーチェック
	int	sslErr = SSL_get_error(m_ssl, sended);
	if(sslErr == SSL_ERROR_ZERO_RETURN)
	{
		//	SSLプロトコル上で切断を検知
		CloseError(CBS_ERROR_CLOSE, "BlockingSend");
		return(-1);
	}
	else if(sslErr != SSL_ERROR_NONE)
	{
		switch(m_socket.GetLastError())
		{
		case COverlappedSocket::OVRS_ERROR_TIMEOUT:
			CloseError(CBS_ERROR_TIMEOUT, _T("BlockingSend"));
			break;

		case COverlappedSocket::OVRS_ERROR_CLOSE:
			CloseError(CBS_ERROR_CLOSE, _T("BlockingSend"));
			break;

		case COverlappedSocket::OVRS_ERROR_BREAK:
			CloseError(CBS_ERROR_BREAK, _T("BlockingSend"));
			break;

		case COverlappedSocket::OVRS_ERROR_OTHER:
			CloseError(CBS_ERROR_OTHER, _T("BlockingSend"));
			break;

		case COverlappedSocket::OVRS_ERROR_NO_ERROR:
		default:
			CloseError(CSPS_ERROR_SSL_OTHER, _T("BlockingSend"));
			break;
		}
		return(-1);
	}

	//	送信したデータ量を設定
	m_sendBuf.SetReadedLen(sended);
	return(m_sendBuf.GetInBuf());
}


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

	//	バッファがあるか？
	if(m_recvBuf.GetFreeBuf() == 0)
		return(m_recvBuf.GetInBuf());	//	データ受信済みなら終了

	//	タイムアウト指定
	m_socket.SetTimeOut(timeOut);

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

	//	受信
	int recved = SSL_read(m_ssl, recvBuf, bufLen);

	//	エラーチェック
	int	sslErr = SSL_get_error(m_ssl, recved);
	if(sslErr == SSL_ERROR_ZERO_RETURN)
	{
		//	SSLプロトコル上で切断を検知
		CloseError(CBS_ERROR_CLOSE, "BlockingSend");
		return(-1);
	}
	else if(sslErr != SSL_ERROR_NONE)
	{
		switch(m_socket.GetLastError())
		{
		case COverlappedSocket::OVRS_ERROR_TIMEOUT:
			CloseError(CBS_ERROR_TIMEOUT, "BlockingRecv");
			break;

		case COverlappedSocket::OVRS_ERROR_CLOSE:
			CloseError(CBS_ERROR_CLOSE, "BlockingSend");
			break;

		case COverlappedSocket::OVRS_ERROR_BREAK:
			CloseError(CBS_ERROR_BREAK, "BlockingRecv");
			break;

		case COverlappedSocket::OVRS_ERROR_OTHER:
			CloseError(CBS_ERROR_OTHER, "BlockingRecv");
			break;

		case COverlappedSocket::OVRS_ERROR_NO_ERROR:
		default:
			CloseError(CSPS_ERROR_SSL_OTHER, "BlockingRecv");
			break;
		}
		return(-1);
	}

	//	受信したデータ量を設定
	m_recvBuf.SetWritedLen(recved);
	return(m_recvBuf.GetInBuf());
}


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

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

	case CSPS_ERROR_SSL_OTHER:
		return(_T("SSL通信プロトコルに関連するエラーが発生しました"));

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