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

//!	BIOラッパ
BIO *BIO_new_ovrs(COverlappedSocket *ovrs);

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_ovrs(&m_socket);
	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);

	//	ネゴシエーション
	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:
	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);
	}

	//	データがある?
	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);
	if(SSL_get_error(m_ssl, sended) != SSL_ERROR_NONE)
	{
		switch(m_socket.GetLastError())
		{
		case COverlappedSocket::OVRS_ERROR_TIMEOUT:
			CloseError(CBS_ERROR_TIMEOUT,"BlockingSend");
			break;

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

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

		case COverlappedSocket::OVRS_ERROR_OTHER:
		default:
			CloseError(CBS_ERROR_OTHER,"BlockingSend");
			break;
		}
		return(-1);
	}

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


/*!
	受信を試す
*/
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);
	if(SSL_get_error(m_ssl, recved) != 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:
		default:
			CloseError(CBS_ERROR_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接続のネゴシエーションに失敗しました"));

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