#include "StdAfx.h"
#include "sslcontext.h"
#include "openssl/rand.h"
#include "openssl/err.h"
#include "openssl/evp.h"

HANDLE	*g_lock_cs = NULL;


CSSLContext::CSSLContext(void)
{
	m_init = 0;
}

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

//	初期化
int CSSLContext::Init(CConfigAccess setting)
{
	CConfigAccess	SSLSetting = setting.GetAccess("SSL");
	CString			privateKey = SSLSetting.GetConfig("PrivateKey","%ServerRoot%SSLKey\\key.pem");
	CString			certificate = SSLSetting.GetConfig("Certificate","%ServerRoot%SSLKey\\ca.crt");

	//	パスワードコピー
	m_password = SSLSetting.GetConfig("Password","test");

	//	ライブラリ初期化
	SSL_library_init();
	SSL_load_error_strings();

	//	コンテキスト生成
	m_method = SSLv23_method();
	m_context = SSL_CTX_new(m_method);
	m_init = 1;

	//	証明書ロード
	if(!SSL_CTX_use_certificate_file(m_context,certificate,SSL_FILETYPE_PEM))
		return(-1);

	//	パスワードコールバック初期化
	SSL_CTX_set_default_passwd_cb(m_context,Password_cb);
	SSL_CTX_set_default_passwd_cb_userdata(m_context,this);

	//	キーロード
    if(!SSL_CTX_use_PrivateKey_file(m_context,privateKey,SSL_FILETYPE_PEM))
		return(-1);

	//	ランダム初期化
	RAND_seed(GetRandomString(10240),10240);

	//	ミューテックス初期化
	InitLock();

	return(0);
}

//	終了
void CSSLContext::Close()
{
	if(m_init)
	{
		m_init = 0;

		SSL_CTX_free(m_context);

		ERR_free_strings();
		ERR_remove_state(0);
		EVP_cleanup();
		CRYPTO_cleanup_all_ex_data();
		EndLock();
	}
}

//	パスワードコールバック
int CSSLContext::Password_cb(char *buf,int num,int rwflag,void *userdata)
{
	CString password = ((CSSLContext *)userdata)->GetPassword();

	//	長さチェック
	if(num < password.GetLength())
		return(0);

	//	パスワード取得
	strcpy(buf,password);
	return(password.GetLength());
}

//	パスワード取得
CString CSSLContext::GetPassword()
{
	SCOPE_LOCK();

	return(m_password);
}


//	ランダム文字列生成
CString CSSLContext::GetRandomString(int count)
{
	DWORD	seed = GetTickCount();
	seed += (DWORD)CTime::GetCurrentTime().GetTime();
	srand(seed);

	CString	random;
	for(int i=0;i<count;i++)
		random += (char)((rand() % 254) + 1);

	return(random);
}

//////////////////////////////////////////////////////////////////////
//	マルチスレッド
//////////////////////////////////////////////////////////////////////
//	ミューテックス初期化
void CSSLContext::InitLock()
{
	if(g_lock_cs == NULL)
	{
		int i;

		g_lock_cs=(HANDLE*)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(HANDLE));
		for (i=0; i<CRYPTO_num_locks(); i++)
			g_lock_cs[i]=CreateMutex(NULL,FALSE,NULL);

		CRYPTO_set_locking_callback(Locking_cb);
	}
}

//	ミューテックス削除
void CSSLContext::EndLock()
{
	if(g_lock_cs)
	{
		int i;

		CRYPTO_set_locking_callback(NULL);
		for (i=0; i<CRYPTO_num_locks(); i++)
			CloseHandle(g_lock_cs[i]);
		OPENSSL_free(g_lock_cs);
		g_lock_cs = NULL;
	}
}

//	ロックコールバック
void  CSSLContext::Locking_cb(int mode, int type,const char *file, int line)
{
	if (mode & CRYPTO_LOCK)
		WaitForSingleObject(g_lock_cs[type],INFINITE);
	else
		ReleaseMutex(g_lock_cs[type]);
}


